/* Orbas:
 *     A open source CORBA Specification implementation from Huihoo.
 *
 * Copyright 2002-2003 Huihoo.org, Inc. All Right Reserved.
 *
 * This software is licensed under LGPL license.
 * See terms of license at gnu.org.
 *
 * For more information, visit:
 *
 * http://www.huihoo.org/orbas
 */

package org.huihoo.orbas.orb;

/**
 * <p>Description: </p>
 * Implementation for Any.
 * <p>Copyright (c) 2002,2003</p>
 * <p>Company: <a href="http://www.huihoo.org/">huihoo.org</a></p>
 * @author <a href="http://www.huihoo.org/~mep">mep(mep@huihoo.com)</a>
 * @see <a href="http://www.huihoo.org/orbas">http://www.huihoo.org/orbas</a>
 * @version 1.0
 */

import org.huihoo.orbas.orb.cdr.CDRInputStream;
import org.huihoo.orbas.orb.cdr.CDROutputStream;
import org.omg.CORBA.BAD_OPERATION;
import org.omg.CORBA.BAD_PARAM;
import org.omg.CORBA.BAD_TYPECODE;
import org.omg.CORBA.Principal;
import org.omg.CORBA.TCKind;
import org.omg.CORBA.TypeCode;

public final class Any extends org.omg.CORBA.Any {

	private org.omg.CORBA.TypeCode typeCode;
	private java.lang.Object value;
	private org.huihoo.orbas.orb.ORB orb;

	Any(org.huihoo.orbas.orb.ORB orb) {
		this.orb = orb;

		typeCode = orb.get_primitive_tc(org.omg.CORBA.TCKind.tk_null);
	}

	public TCKind kind() {
		return typeCode.kind();
	}

	public org.omg.CORBA.TypeCode type() {
		return typeCode;
	}

	public void type(org.omg.CORBA.TypeCode t) {
		typeCode = t;
		value = null;
	}

	public boolean equal(org.omg.CORBA.Any a) {

		if (a == null) {
			throw new BAD_PARAM("Null passed to Any equal operation");
		}

		if (!typeCode.equal(a.type())) {
			return false;
		}

		// get the real type
		TypeCode tc = typeCode;

		try {
			while (tc.kind() == org.omg.CORBA.TCKind.tk_alias)
				tc = tc.content_type();
		} catch (org.omg.CORBA.TypeCodePackage.BadKind bk) {
			// does not happen
		}

		int kind = tc.kind().value();

		switch (kind) {
		case TCKind._tk_null:
		case TCKind._tk_void:
			return true;
		case TCKind._tk_short:
			return extract_short() == a.extract_short();
		case TCKind._tk_long:
			return extract_long() == a.extract_long();
		case TCKind._tk_longlong:
			return extract_longlong() == a.extract_longlong();
		case TCKind._tk_ushort:
			return extract_ushort() == a.extract_ushort();
		case TCKind._tk_ulong:
			return extract_ulong() == a.extract_ulong();
		case TCKind._tk_ulonglong:
			return extract_ulonglong() == a.extract_ulonglong();
		case TCKind._tk_float:
			return extract_float() == a.extract_float();
		case TCKind._tk_double:
			return extract_double() == a.extract_double();
		case TCKind._tk_fixed:
			throw new org.omg.CORBA.NO_IMPLEMENT();
			// return;
		case TCKind._tk_longdouble:
			throw new org.omg.CORBA.NO_IMPLEMENT();
			// return;
		case TCKind._tk_boolean:
			return extract_boolean() == a.extract_boolean();
		case TCKind._tk_char:
			return extract_char() == a.extract_char();
		case TCKind._tk_wchar:
			return extract_wchar() == a.extract_wchar();
		case TCKind._tk_octet:
			return extract_octet() == a.extract_octet();
		case TCKind._tk_any:
			return extract_any().equals(a.extract_any());
		case TCKind._tk_TypeCode:
			return extract_TypeCode().equal(a.extract_TypeCode());
		case TCKind._tk_Principal:
			throw new org.omg.CORBA.NO_IMPLEMENT("Principal deprecated");
		case TCKind._tk_objref:
			return extract_Object().equals(a.extract_Object());
		case TCKind._tk_string:
			return extract_string().equals(a.extract_string());
		case TCKind._tk_wstring:
			return extract_wstring().equals(a.extract_wstring());
		case TCKind._tk_array:
		case TCKind._tk_sequence:
		case TCKind._tk_struct:
		case TCKind._tk_except:
		case TCKind._tk_enum:
		case TCKind._tk_union: {
			CDROutputStream out1, out2;

			out1 = new CDROutputStream(orb);
			out2 = new CDROutputStream(orb);

			write_value(out1);
			a.write_value(out2);

			return java.util.Arrays.equals(out1.getBuffer(), out2.getBuffer());

		}
		default:
			throw new BAD_TYPECODE("Cannot compare anys with TypeCode kind "
					+ kind);
		}
	}

	public void read_value(org.omg.CORBA.portable.InputStream is,
			org.omg.CORBA.TypeCode type) {
		// get the real type
		TypeCode tc = type;

		this.typeCode = type;

		try {
			while (tc.kind() == org.omg.CORBA.TCKind.tk_alias)
				tc = tc.content_type();
		} catch (org.omg.CORBA.TypeCodePackage.BadKind bk) {
			// does not happen
		}

		int kind = tc.kind().value();

		switch (kind) {
		case TCKind._tk_null:
		case TCKind._tk_void:
			value = null;
			return;
		case TCKind._tk_short:
			value = new Short(is.read_short());
			return;
		case TCKind._tk_long:
			value = new Integer(is.read_long());
			return;
		case TCKind._tk_ushort:
			value = new Short(is.read_ushort());
			return;
		case TCKind._tk_ulong:
			value = new Integer(is.read_ulong());
			return;
		case TCKind._tk_float:
			value = new Float(is.read_float());
			return;
		case TCKind._tk_double:
			value = new Double(is.read_double());
			return;
		case TCKind._tk_boolean:
			value = new Boolean(is.read_boolean());
			return;
		case TCKind._tk_char:
			value = new Character(is.read_char());
			return;
		case TCKind._tk_octet:
			value = new Byte(is.read_octet());
			return;
		case TCKind._tk_TypeCode:
			value = is.read_TypeCode();
			return;
		case TCKind._tk_Principal:
			throw new org.omg.CORBA.NO_IMPLEMENT("Principal deprecated");
			// return;
		case TCKind._tk_objref:
			value = is.read_Object();
			// it's not enough,we must set ids
			// so that we can compare it with is_equivalent
			org.huihoo.orbas.orb.ObjectImpl impl = (org.huihoo.orbas.orb.ObjectImpl) value;

			try {
				String[] ids = new String[2];
				ids[0] = "IDL:org/omg/Object:1.0";
				ids[1] = this.typeCode.id();

				impl.set_ids(ids);
				value = impl;
			} catch (org.omg.CORBA.TypeCodePackage.BadKind e) {
				System.out.println("Any read_value : Bad Interface TypeCode");
			}

			return;
		case TCKind._tk_string:
			value = is.read_string();
			return;
		case TCKind._tk_longlong:
			value = new Long(is.read_longlong());
			return;
		case TCKind._tk_ulonglong:
			value = new Long(is.read_ulonglong());
			return;
		case TCKind._tk_longdouble:
			throw new org.omg.CORBA.NO_IMPLEMENT();
			// return;
		case TCKind._tk_wchar:
			value = new Character(is.read_wchar());
			return;
		case TCKind._tk_wstring:
			value = is.read_wstring();
			return;
		case TCKind._tk_any:
			if (value == null)
				value = orb.create_any();
			((Any) value).read_value(is, is.read_TypeCode());
			return;
		case TCKind._tk_abstract_interface:
			throw new org.omg.CORBA.NO_IMPLEMENT();
			// return;
		case TCKind._tk_local_interface:
			throw new org.omg.CORBA.NO_IMPLEMENT();
			// return;
		case TCKind._tk_fixed:
			throw new org.omg.CORBA.NO_IMPLEMENT();
			// return;
		case TCKind._tk_value:
			throw new org.omg.CORBA.NO_IMPLEMENT();
			// return;
		case TCKind._tk_value_box:
			throw new org.omg.CORBA.NO_IMPLEMENT();
			// return;
		case TCKind._tk_enum:
			value = new Integer(is.read_ulong());
			return;
		case TCKind._tk_except:
		case TCKind._tk_struct:
		case TCKind._tk_sequence:
		case TCKind._tk_array:
			// dont know how to implement
			CDROutputStream out = new CDROutputStream(orb);

			// extract typecode and read every member from inputstream
			try {
				for (int i = 0; i < tc.member_count(); i++) {
					org.omg.CORBA.Any mem = orb.create_any();

					mem.read_value(is, tc.member_type(i));
					mem.write_value(out);

					mem = null;
				}
			} catch (org.omg.CORBA.TypeCodePackage.BadKind eb) {
				System.out.println("Read any Error: " + eb.toString());
			} catch (org.omg.CORBA.TypeCodePackage.Bounds ex) {
				System.out.println("Read any Error: " + ex.toString());
			}

			value = out;
			return;
		case TCKind._tk_union:
			CDROutputStream unionOut = new CDROutputStream(orb);

			// read discriminator
			try {
				org.omg.CORBA.Any discriminator = orb.create_any();
				discriminator.read_value(is, tc.discriminator_type());
				discriminator.write_value(unionOut);

				boolean flag = false;

				for (int i = 0; i < tc.member_count(); i++) {

					if (tc.member_label(i).equal(discriminator)) {
						org.omg.CORBA.Any mem = orb.create_any();
						mem.read_value(is, tc.member_type(i));

						mem.write_value(unionOut);

						mem = null;
						flag = true;

						break;
					}

				}

				if (flag == false) {
					System.out.println("Any : No Such Discriminator Value");
				}

			} catch (org.omg.CORBA.TypeCodePackage.BadKind eb) {
				System.out.println("Read any Error: " + eb.toString());
			} catch (org.omg.CORBA.TypeCodePackage.Bounds ex) {
				System.out.println("Read any Error: " + ex.toString());
			}

			value = unionOut;
			return;
		case TCKind._tk_alias:
		case TCKind._tk_native:
		default:
			// error
			System.out.println("Any Invalid TCKind code read: " + kind);
			return;
		}
	}

	public void write_value(org.omg.CORBA.portable.OutputStream os) {

		// get the real type
		TypeCode tc = typeCode;

		try {
			while (tc.kind() == org.omg.CORBA.TCKind.tk_alias)
				tc = tc.content_type();
		} catch (org.omg.CORBA.TypeCodePackage.BadKind bk) {
			// does not happen
		}

		int kind = tc.kind().value();

		switch (kind) {
		case TCKind._tk_null:
		case TCKind._tk_void:
			return;
		case TCKind._tk_short:
			os.write_short(((Short) value).shortValue());
			return;
		case TCKind._tk_ushort:
			os.write_ushort(((Short) value).shortValue());
			return;
		case TCKind._tk_long:
			os.write_long(((Integer) value).intValue());
			return;
		case TCKind._tk_ulong:
			os.write_ulong(((Integer) value).intValue());
			return;
		case TCKind._tk_float:
			os.write_float(((Float) value).floatValue());
			return;
		case TCKind._tk_double:
			os.write_double(((Double) value).doubleValue());
			return;
		case TCKind._tk_boolean:
			os.write_boolean(((Boolean) value).booleanValue());
			return;
		case TCKind._tk_char:
			os.write_char(((Character) value).charValue());
			return;
		case TCKind._tk_octet:
			os.write_octet(((Byte) value).byteValue());
			return;
		case TCKind._tk_TypeCode:
			os.write_TypeCode((TypeCode) value);
			return;
		case TCKind._tk_Principal:
			throw new org.omg.CORBA.NO_IMPLEMENT("Principal deprecated");
			// return;
		case TCKind._tk_objref:
			os.write_Object((org.omg.CORBA.Object) value);
			return;
		case TCKind._tk_string:
			os.write_string((String) value);
			return;
		case TCKind._tk_longlong:
			os.write_longlong(((Long) value).longValue());
			return;
		case TCKind._tk_ulonglong:
			os.write_ulonglong(((Long) value).longValue());
			return;
		case TCKind._tk_longdouble:
			throw new org.omg.CORBA.NO_IMPLEMENT();
			// return;
		case TCKind._tk_wchar:
			os.write_wchar(((Character) value).charValue());
			return;
		case TCKind._tk_wstring:
			os.write_wstring((String) value);
			return;
		case TCKind._tk_any:
			os.write_TypeCode(((Any) value).type());
			((Any) value).write_value(os);
			return;
		case TCKind._tk_abstract_interface:
			throw new org.omg.CORBA.NO_IMPLEMENT();
			// return;
		case TCKind._tk_local_interface:
			throw new org.omg.CORBA.NO_IMPLEMENT();
			// return;
		case TCKind._tk_value:
			throw new org.omg.CORBA.NO_IMPLEMENT();
			// return;
		case TCKind._tk_value_box:
			throw new org.omg.CORBA.NO_IMPLEMENT();
			// return;
		case TCKind._tk_enum:
			os.write_ulong(((Integer) value).intValue());
			return;
		case TCKind._tk_fixed:
			throw new org.omg.CORBA.NO_IMPLEMENT();
			// return;
		case TCKind._tk_struct:
		case TCKind._tk_union:
		case TCKind._tk_except:
		case TCKind._tk_sequence:
		case TCKind._tk_array:
			if (!(value instanceof CDROutputStream)) {
				System.out.println("Internal Error of Any");
			}

			// try {
			// for( int i = 0; i < tc.member_count(); i++ ) {
			// org.omg.CORBA.Any mem = orb.create_any();
			//
			// mem.read_value(is, tc.member_type(i));
			// mem.write_value(out);
			// }
			// }
			// catch(org.omg.CORBA.TypeCodePackage.BadKind eb) {
			// System.out.println("Read any Error: " + eb.toString());
			// }
			// catch(org.omg.CORBA.TypeCodePackage.Bounds ex) {
			// System.out.println("Read any Error: " + ex.toString());
			// }

			CDROutputStream out = (CDROutputStream) value;
			os.write_octet_array(out.getBuffer(), 0, out.getLength() - 1);
			return;
		case TCKind._tk_alias:
		case TCKind._tk_native:
		default:
			// error;
			System.out.println("Any Invalid TCKind code write: " + kind);
			return;
		}
	}

	public org.omg.CORBA.portable.OutputStream create_output_stream() {

		value = new CDROutputStream(orb);

		return (CDROutputStream) value;
	}

	public org.omg.CORBA.portable.InputStream create_input_stream() {

		CDROutputStream out;
		out = new CDROutputStream(orb);
		write_value(out);

		return new CDRInputStream(orb, out.getBuffer());
	}

	public short extract_short() {

		checkTypeCode(TCKind._tk_short);

		if (value instanceof CDROutputStream) {
			return create_input_stream().read_short();
		}

		return ((Short) value).shortValue();
	}

	public void insert_short(short s) {

		value = new Short(s);
		typeCode = orb.get_primitive_tc(TCKind.tk_short);
	}

	public int extract_long() {

		checkTypeCode(TCKind._tk_long);

		if (value instanceof CDROutputStream) {
			return create_input_stream().read_long();
		}

		return ((Integer) value).intValue();
	}

	public void insert_long(int i) {

		value = new Integer(i);
		typeCode = orb.get_primitive_tc(TCKind.tk_long);
	}

	public long extract_longlong() {

		checkTypeCode(TCKind._tk_longlong);

		if (value instanceof CDROutputStream) {
			return create_input_stream().read_longlong();
		}

		return ((Long) value).longValue();
	}

	public void insert_longlong(long l) {

		value = new Long(l);
		typeCode = orb.get_primitive_tc(TCKind.tk_longlong);
	}

	public short extract_ushort() {

		checkTypeCode(TCKind._tk_ushort);

		if (value instanceof CDROutputStream) {
			return create_input_stream().read_ushort();
		}

		return ((Short) value).shortValue();
	}

	public void insert_ushort(short s) {

		value = new Short(s);
		typeCode = orb.get_primitive_tc(TCKind.tk_ushort);
	}

	public int extract_ulong() {

		checkTypeCode(TCKind._tk_ulong);

		if (value instanceof CDROutputStream) {
			return create_input_stream().read_ulong();
		}

		return ((Integer) value).intValue();
	}

	public void insert_ulong(int i) {

		value = new Integer(i);
		typeCode = orb.get_primitive_tc(TCKind.tk_ulong);
	}

	public long extract_ulonglong() {

		checkTypeCode(TCKind._tk_ulonglong);

		if (value instanceof CDROutputStream) {
			return create_input_stream().read_ulonglong();
		}

		return ((Long) value).longValue();
	}

	public void insert_ulonglong(long l) {

		value = new Long(l);
		typeCode = orb.get_primitive_tc(TCKind.tk_ulonglong);
	}

	public float extract_float() {

		checkTypeCode(TCKind._tk_float);

		if (value instanceof CDROutputStream) {
			return create_input_stream().read_float();
		}

		return ((Float) value).floatValue();

	}

	public void insert_float(float f) {

		value = new Float(f);
		typeCode = orb.get_primitive_tc(TCKind.tk_float);
	}

	public double extract_double() {

		checkTypeCode(TCKind._tk_double);

		if (value instanceof CDROutputStream) {
			return create_input_stream().read_double();
		}

		return ((Double) value).doubleValue();
	}

	public void insert_double(double d) {

		value = new Double(d);
		typeCode = orb.get_primitive_tc(TCKind.tk_double);
	}

	public boolean extract_boolean() {

		checkTypeCode(TCKind._tk_boolean);

		if (value instanceof CDROutputStream) {
			return create_input_stream().read_boolean();
		}

		return ((Boolean) value).booleanValue();
	}

	public void insert_boolean(boolean b) {

		value = new Boolean(b);
		typeCode = orb.get_primitive_tc(TCKind.tk_boolean);
	}

	public char extract_char() {

		checkTypeCode(TCKind._tk_char);

		if (value instanceof CDROutputStream) {
			return create_input_stream().read_char();
		}

		return ((Character) value).charValue();
	}

	public void insert_char(char c) {

		value = new Character(c);
		typeCode = orb.get_primitive_tc(TCKind.tk_char);
	}

	public char extract_wchar() {

		checkTypeCode(TCKind._tk_wchar);

		if (value instanceof CDROutputStream) {
			return create_input_stream().read_wchar();
		}

		return ((Character) value).charValue();
	}

	public void insert_wchar(char c) {

		value = new Character(c);
		typeCode = orb.get_primitive_tc(TCKind.tk_wchar);
	}

	public byte extract_octet() {

		checkTypeCode(TCKind._tk_octet);

		if (value instanceof CDROutputStream) {
			return create_input_stream().read_octet();
		}

		return ((Byte) value).byteValue();
	}

	public void insert_octet(byte b) {

		value = new Byte(b);
		typeCode = orb.get_primitive_tc(TCKind.tk_octet);
	}

	public org.omg.CORBA.Any extract_any() {

		checkTypeCode(TCKind._tk_any);

		if (value instanceof CDROutputStream) {
			return create_input_stream().read_any();
		}

		return (org.omg.CORBA.Any) value;
	}

	public void insert_any(org.omg.CORBA.Any a) {

		value = a;
		typeCode = orb.get_primitive_tc(TCKind.tk_any);
	}

	public org.omg.CORBA.Object extract_Object() {

		checkTypeCode(TCKind._tk_objref);

		if (value instanceof CDROutputStream) {
			return create_input_stream().read_Object();
		}

		return (org.omg.CORBA.Object) value;
	}

	public void insert_Object(org.omg.CORBA.Object obj) {

		value = obj;

		String typeId = null;
		String name = "";

		if (value == null) {
			typeId = "IDL:omg.org/CORBA/Object:1.0";
			name = "Object";
		} else {
			typeId = ((org.omg.CORBA.portable.ObjectImpl) obj)._ids()[0];

			// check if the repository Id is in IDL format
			if (typeId.startsWith("IDL:")) {
				// parse the name from the repository Id string
				name = typeId.substring(4, typeId.lastIndexOf(':'));
				name = name.substring(name.lastIndexOf('/') + 1);
			}
		}

		typeCode = orb.create_interface_tc(typeId, name);
	}

	public java.io.Serializable extract_Value() {
		throw new org.omg.CORBA.NO_IMPLEMENT();
	}

	public void insert_Value(java.io.Serializable v) {
		throw new org.omg.CORBA.NO_IMPLEMENT();
	}

	public void insert_Value(java.io.Serializable v, org.omg.CORBA.TypeCode t) {
		throw new org.omg.CORBA.NO_IMPLEMENT();
	}

	public void insert_Object(org.omg.CORBA.Object obj,
			org.omg.CORBA.TypeCode type) {

		value = obj;
		this.typeCode = type;
	}

	public String extract_string() {

		checkTypeCode(TCKind._tk_string);

		return value.toString();
	}

	public void insert_string(String s) {

		value = s;
		typeCode = orb.create_string_tc(0);
	}

	public String extract_wstring() {

		checkTypeCode(TCKind._tk_wstring);

		return value.toString();
	}

	public void insert_wstring(String value) {

		this.value = value;
		typeCode = orb.create_wstring_tc(0);
	}

	public TypeCode extract_TypeCode() {

		checkTypeCode(TCKind._tk_TypeCode);

		if (value instanceof CDROutputStream) {
			return create_input_stream().read_TypeCode();
		}

		return (org.omg.CORBA.TypeCode) value;
	}

	public void insert_TypeCode(TypeCode value) {

		this.value = value;
		typeCode = orb.get_primitive_tc(TCKind.tk_TypeCode);
	}

	/**
	 * @deprecated
	 */
	public Principal extract_Principal() {
		throw new org.omg.CORBA.NO_IMPLEMENT();
	}

	/**
	 * @deprecated
	 */
	public void insert_Principal(Principal p) {
		throw new org.omg.CORBA.NO_IMPLEMENT();
	}

	public org.omg.CORBA.portable.Streamable extract_Streamable() {
		throw new org.omg.CORBA.NO_IMPLEMENT();
	}

	public void insert_Streamable(org.omg.CORBA.portable.Streamable s) {
		throw new org.omg.CORBA.NO_IMPLEMENT();
	}

	public java.math.BigDecimal extract_fixed() {
		throw new org.omg.CORBA.NO_IMPLEMENT();
	}

	public void insert_fixed(java.math.BigDecimal f, org.omg.CORBA.TypeCode t) {
		throw new org.omg.CORBA.NO_IMPLEMENT();
	}

	private void checkTypeCode(int kind) {

		try {
			if (typeCode.content_type().kind().value() != kind) {
				throw new BAD_OPERATION("None Compatible typecode");
			}
		} catch (org.omg.CORBA.TypeCodePackage.BadKind e) {
			return;
		} catch (org.omg.CORBA.NO_IMPLEMENT ex) {
			return;
		}
	}
}